package org.example.service.impl;

import cn.hutool.core.collection.ConcurrentHashSet;
import cn.hutool.core.text.StrFormatter;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.example.config.ExecutorConfig;
import org.example.config.XxlJobConfig;
import org.example.entity.dto.XxlJobGroupResponse;
import org.example.entity.dto.XxlJobInfo;
import org.example.entity.dto.XxlJobInfoReq;
import org.example.service.XxlJobExecutorApi;
import org.example.util.Opt;
import org.example.util.Opts;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.time.Duration;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @author zengxiaoyu
 * @date 2025/9/12 9:20
 */
@Component
@Slf4j
public class XxlJobExecutorApiImpl implements XxlJobExecutorApi {

    // RestTemplate实例是线程安全的，可以在多线程环境下共享使用
    private static final RestTemplate restTemplate = new RestTemplate();
    public static final Map<String, Integer> XXL_JOB_EXECUTOR_NAME_MAP = new ConcurrentHashMap<>();
    public static final Map<String,XxlJobInfoReq> XXL_JOB_NAME_MAP = new ConcurrentHashMap<>();


    @Autowired
    private XxlJobConfig xxlJobConfig;

    /**
     * 获取所有执行器
     */
    public XxlJobGroupResponse getAllExecutors(String appName) {
        String cookie = loginAndGetCookie();
        if (cookie == null) {
            throw new RuntimeException("登录 XXL-Job Admin 失败");
        }
        Map<String, Object> params = new HashMap<>();
        params.put("start", 0);
        params.put("length", 1000); // 获取所有数据
        params.put("appname", Opt.of(appName).orElse(""));
        params.put("title", "");
        RestTemplate restTemplate = new RestTemplateBuilder()
                .defaultHeader("Cookie", cookie)
                .setConnectTimeout(Duration.ofSeconds(10))
                .setReadTimeout(Duration.ofSeconds(30))
                .build();

        // 发送 GET 请求
        ResponseEntity<String> response = restTemplate.getForEntity(
                xxlJobConfig.getAdminAddresses() + StrFormatter.format("jobgroup/pageList?start={}&length={}&appname={}&title={}",
                        params.get("start"), params.get("length"), params.get("appname"), params.get("title")),
                String.class
        );


        Opt<XxlJobGroupResponse> xxlJobGroupResponse = Opt.of(response).exec(t -> {
            if (!Objects.equals(Opt.of(t.getStatusCode()).exec(HttpStatus::value).orElse(0), 200)) {
                throw new RuntimeException("获取执行器列表失败:msg:" + JSON.toJSONString(t));
            }
            return JSON.parseObject(t.getBody(), XxlJobGroupResponse.class);
        });
        if (!xxlJobGroupResponse.isPresent()) {
            throw new RuntimeException("获取执行器列表失败" + Opt.of(response).exec(JSON::toJSONString).orElse(""));
        }
        XxlJobGroupResponse xxlJobGroupResponse1 = xxlJobGroupResponse.get();
        Opt.of(xxlJobGroupResponse1).ofs(XxlJobGroupResponse::getData).action(ts -> {
            ts.forEach(f -> XXL_JOB_EXECUTOR_NAME_MAP.put(f.getAppname(), f.getId()));
        });
        return xxlJobGroupResponse1;
    }

    @Override
    public boolean createExecutor(ExecutorConfig executor) {
        if (XXL_JOB_EXECUTOR_NAME_MAP.containsKey(executor.getAppName())) {
            log.info("xxlJobExecutor:{} has been registered", executor.getAppName());
            return true;
        }
        synchronized (XXL_JOB_EXECUTOR_NAME_MAP) {
            if (XXL_JOB_EXECUTOR_NAME_MAP.containsKey(executor.getAppName())) {
                log.info("xxlJobExecutor:{} has been registered", executor.getAppName());
                return true;
            }
            try {
                // 1. 构建API URL
                String apiUrl = xxlJobConfig.getAdminAddresses() + "jobgroup/save";

                // 2. 准备请求参数
                MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
                params.add("appname", executor.getAppName());
                params.add("title", executor.getTitle());
                params.add("addressType", 0);
                // 3. 设置请求头
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
                headers.add("Cookie", loginAndGetCookie());
                // 4. 创建请求实体
                HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(params, headers);

                // 5. 发送请求
                ResponseEntity<String> response = restTemplate.exchange(
                        apiUrl,
                        HttpMethod.POST,
                        requestEntity,
                        String.class
                );
                // 6. 处理响应
                if (response.getStatusCode() == HttpStatus.OK) {
                    Map<String, Object> responseBody = JSON.parseObject(response.getBody(), new TypeReference<Map<String, Object>>() {
                    });
                    if (responseBody != null && "200".equals(String.valueOf(responseBody.get("code")))) {
                        System.out.println("执行器创建成功: " + executor.getAppName());
                        getAllExecutors(executor.getAppName());
                        return true;
                    } else {
                        System.out.println("执行器创建失败: " + responseBody);
                    }
                } else {
                    System.out.println("HTTP请求失败: " + response.getStatusCode());
                }
            } catch (Exception e) {
                System.err.println("创建执行器时出错: " + e.getMessage());
                throw new RuntimeException(e);
            }
        }
        return false;
    }

    @Override
    public Integer getJobGroupId(String appName) {
        Integer jobGroupId = XXL_JOB_EXECUTOR_NAME_MAP.get(appName);
        if (jobGroupId == null) {
            getAllExecutors(null);
            jobGroupId = XXL_JOB_EXECUTOR_NAME_MAP.get(appName);
            if (jobGroupId == null) {
                throw new RuntimeException("找不到执行器ID,appName:" + appName);
            }
        }
        return jobGroupId;
    }

    @Override
    public void createJobIfNotExist(XxlJobInfoReq xxlJobInfoReq) {
        if (XXL_JOB_NAME_MAP.containsKey(xxlJobInfoReq.getJobDesc())) {
            log.info("xxlJob:{} has been registered", xxlJobInfoReq.getJobDesc());
            return;
        }
        synchronized (XXL_JOB_NAME_MAP) {
            if (XXL_JOB_NAME_MAP.containsKey(xxlJobInfoReq.getJobDesc())) {
                log.info("xxlJob:{} has been registered", xxlJobInfoReq.getJobDesc());
                return;
            }
            List<XxlJobInfo> jobs = findJob(xxlJobInfoReq.getJobDesc(), xxlJobInfoReq.getAuthor(), true);
            if (jobs != null && !jobs.isEmpty()) {
                XXL_JOB_NAME_MAP.put(xxlJobInfoReq.getJobDesc(),xxlJobInfoReq);
                log.info("xxlJob:{} has been registered where been query", xxlJobInfoReq.getJobDesc());
                // 比较任务的关键参数
                boolean isMatch = compareJobInfo(jobs.get(0), xxlJobInfoReq);
                if (!isMatch) {
                    log.info("xxlJob:{} has been registered where been query,but not match", xxlJobInfoReq.getJobDesc());
                    removeJobs(jobs);
                    addJob(xxlJobInfoReq);
                } else {
                    log.info("xxlJob:{} has been registered where been query,and match", xxlJobInfoReq.getJobDesc());
                }
            } else {
                addJob(xxlJobInfoReq);
                XXL_JOB_NAME_MAP.put(xxlJobInfoReq.getJobDesc(),xxlJobInfoReq);
            }

        }

    }

    private boolean removeJobs(List<XxlJobInfo> jobs) {
        Opts.of(jobs)
                .action(ts -> {
                    for (XxlJobInfo job : ts) {
                        RestTemplate restTemplate = new RestTemplateBuilder()
                                .defaultHeader("Cookie", loginAndGetCookie())
                                .setConnectTimeout(Duration.ofSeconds(10))
                                .setReadTimeout(Duration.ofSeconds(30))
                                .build();
                        ResponseEntity<String> response = restTemplate.getForEntity(
                                xxlJobConfig.getAdminAddresses() + StrFormatter.format("jobinfo/remove?id={}",
                                        job.getId())
                                ,
                                String.class
                        );
                        // 解析响应
                        if (response.getStatusCode() == HttpStatus.OK) {
                            log.info("移除 job:{},返回值", job.getJobDesc(), response.getBody());

                        }
                    }
                });
        return true;
    }


    private boolean updateJob(XxlJobInfo job, XxlJobInfoReq xxlJobInfoReq) {
        // 1. 构建API URL
        String apiUrl = xxlJobConfig.getAdminAddresses() + "jobinfo/update";
        xxlJobInfoReq.setExecutorParam(job.getExecutorParam());
        cn.hutool.json.JSONObject paramObj = JSONUtil.parseObj(JSONUtil.toJsonStr(xxlJobInfoReq));
        // 2. 准备请求参数
        MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
        for (String s : paramObj.keySet()) {
            params.add(s, paramObj.get(s));
        }
        // 3. 设置请求头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        headers.add("Cookie", loginAndGetCookie());
        // 4. 创建请求实体
        HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(params, headers);
        // 5. 发送请求
        ResponseEntity<String> response = restTemplate.exchange(
                apiUrl,
                HttpMethod.POST,
                requestEntity,
                String.class
        );
        // 6. 处理响应
        if (response.getStatusCode() == HttpStatus.OK) {
            Map<String, Object> responseBody = JSON.parseObject(response.getBody(), new TypeReference<Map<String, Object>>() {
            });
            if (responseBody != null && "200".equals(String.valueOf(responseBody.get("code")))) {
                System.out.println("xxlJob更新成功: " + job.getJobDesc());
                return true;
            } else {
                System.out.println("xxlJob更新失败: " + responseBody);
                throw new RuntimeException("更新任务失败" + responseBody);
            }
        } else {
            System.out.println("HTTP请求失败: " + response.getStatusCode());
            throw new RuntimeException("更新任务失败" + response);
        }
    }

    private void addJob(XxlJobInfoReq xxlJobInfoReq) {
        // 1. 构建API URL
        String apiUrl = xxlJobConfig.getAdminAddresses() + "jobinfo/add";
        cn.hutool.json.JSONObject paramObj = JSONUtil.parseObj(JSONUtil.toJsonStr(xxlJobInfoReq));
        // 2. 准备请求参数
        MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
        for (String s : paramObj.keySet()) {
            params.add(s, paramObj.get(s));
        }
        // 3. 设置请求头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        headers.add("Cookie", loginAndGetCookie());
        // 4. 创建请求实体
        HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(params, headers);
        // 5. 发送请求
        ResponseEntity<String> response = restTemplate.exchange(
                apiUrl,
                HttpMethod.POST,
                requestEntity,
                String.class
        );
        // 6. 处理响应
        if (response.getStatusCode() == HttpStatus.OK) {
            Map<String, Object> responseBody = JSON.parseObject(response.getBody(), new TypeReference<Map<String, Object>>() {
            });
            if (responseBody != null && "200".equals(String.valueOf(responseBody.get("code")))) {
                System.out.println("xxlJob创建成功: " + xxlJobInfoReq.getJobDesc());
            } else {
                System.out.println("xxlJob更新失败: " + responseBody);
                throw new RuntimeException("更新任务失败" + responseBody);
            }
        } else {
            System.out.println("HTTP请求失败: " + response.getStatusCode());
            throw new RuntimeException("更新任务失败" + response);
        }
    }

    private boolean compareJobInfo(XxlJobInfo job, XxlJobInfoReq xxlJobInfoReq) {
        return job.getScheduleConf().equals(xxlJobInfoReq.getScheduleConf())
                && job.getExecutorHandler().equals(xxlJobInfoReq.getExecutorHandler())
                && job.getJobGroup() == xxlJobInfoReq.getJobGroup()
                && job.getAuthor().equals(xxlJobInfoReq.getAuthor());

    }


    private String loginAndGetCookie() {
        // 准备登录参数
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("userName", xxlJobConfig.getUserName());
        params.add("password", xxlJobConfig.getPassword());

        // 发送登录请求到正确的 /doLogin 端点
        ResponseEntity<String> response = restTemplate.postForEntity(
                xxlJobConfig.getAdminAddresses() + "auth/doLogin", // 关键修正点
                params,
                String.class
        );

        // 从响应头获取Cookie
        if (response.getStatusCode() == HttpStatus.OK) {
            // 解析响应体确认登录成功
            JSONObject responseBody = JSON.parseObject(response.getBody());
            if (responseBody != null && responseBody.getInteger("code") == 200) {
                HttpHeaders headers = response.getHeaders();
                List<String> cookies = headers.get(HttpHeaders.SET_COOKIE);
                if (cookies != null && !cookies.isEmpty()) {
                    // 查找包含 XXL_JOB_LOGIN_IDENTITY 的 Cookie
                    for (String cookie : cookies) {
                        if (cookie.contains("xxl_job_login_token")) {
                            return cookie;
                        }
                    }
                }
            }
        }
        return null;
    }


    /**
     * 查找任务ID（根据执行器ID和任务处理器名称）
     */
    private List<XxlJobInfo> findJob(String jobDesc, String author, boolean filterJobDesc) {
        RestTemplate restTemplate = new RestTemplateBuilder()
                .defaultHeader("Cookie", loginAndGetCookie())
                .setConnectTimeout(Duration.ofSeconds(10))
                .setReadTimeout(Duration.ofSeconds(30))
                .build();
        ResponseEntity<String> response = restTemplate.getForEntity(
                xxlJobConfig.getAdminAddresses() + StrFormatter.format("jobinfo/pageList?start=0&length=100&jobGroup={}&executorHandler={}&jobDesc={}&triggerStatus={}&author={}",
                        -1, "", filterJobDesc ? jobDesc : "", -1, author)
                ,
                String.class
        );
        // 解析响应
        if (response.getStatusCode() == HttpStatus.OK) {
            JSONObject json = JSON.parseObject(response.getBody());
            JSONArray data = json.getJSONArray("data");
            if (data != null && json.getInteger("recordsTotal") > 0) {
                // 返回第一个匹配的任务ID
                return JSONUtil.toList(JSONUtil.toJsonStr(data), XxlJobInfo.class);
            }
        }
        return null;
    }

    @Override
    public void removeUnusedJob(String author) {
        List<XxlJobInfo> jobs = findJob("", author, false);
        Opts.of(jobs).action(js->{
            // 无效任务，直接删除
            List<XxlJobInfo> invalidJobs = new ArrayList<>();
            for (XxlJobInfo job : js) {
                if(!XXL_JOB_NAME_MAP.containsKey(job.getJobDesc()) ){
                    invalidJobs.add(job);
                }else{
                    XxlJobInfoReq xxlJobInfoReq = XXL_JOB_NAME_MAP.get(job.getJobDesc());
                    if(xxlJobInfoReq.getJobGroup() != job.getJobGroup() || !xxlJobInfoReq.getExecutorHandler().equals(job.getExecutorHandler())){
                        invalidJobs.add(job);
                    }
                }
            }
            Opts.of(invalidJobs).action(this::removeJobs);
        });
    }

}
